from copy import deepcopy
import random

# The side length of the square board:
SIDE = 8#32

# The board is represented by a list with CELL_CNT count:
CELL_CNT = SIDE * SIDE

# The number of randomly generated morsels:
MORSEL_CNT = SIDE*3 #89

# Read-only direcrions
UP      = "A"
DOWN    = "V"
RIGHT   = ">"
LEFT    = "<"

##Classes of possible Board Positions
class BoardPosition(object):
    def __init__(self, ID, board):
        self.ID = ID
        self.board = board
      
class Ant(BoardPosition):
    """ Represents ants
    """
    def __init__(self, ID, board, direction):
        """ Contructor """
        BoardPosition.__init__(self, ID, board)
        self.direction = direction

    def __eq__(self, other):
        return self.ID == other.ID
    
    def __str__(self):
        return self.direction
        
    def __repr__(self):
        return self.__str__()

    def advance(self):
        """ Moves ahead.
        If a morsel is ahead, replaces it with an empty cell.
        """
        new_pos = index_from(self.direction, self.ID)
        self.board.swap_position(self.ID, new_pos)

    def turn_right(self):
        """ Turns right
        """
        self.direction = turn_direction_right(self.direction)

    def turn_left(self):
        """ Turns left
        """
        self.direction = turn_direction_left(self.direction)

    def is_morsel_to_the(self, direction):
        """ Looks if there is a morsel in the given direction.
        """
        (new_i, new_j) = index_from(direction, self.ID)
        if self.board.legal_position((new_i, new_j)):
                return type(self.board.board[new_i][new_j]) == Morsel
        return False
        
    def is_morsel_ahead(self):
        """ Looks if there is a morsel ahead of the ant.
        """
        return self.is_morsel_to_the(self.direction)

    def is_morsel_right(self):
        """ Looks if there is a morsel to the right of the ant.
        """
        return self.is_morsel_to_the(turn_direction_right(self.direction))
        
    def is_morsel_left(self):
        """ Looks if there is a morsel to the left of the ant.
        """
        return self.is_morsel_to_the(turn_direction_left(self.direction))
        
class Morsel(BoardPosition):
    def __eq__(self, other):
        return self.ID == other.ID
       
    def __str__(self):
        return 'o'
    
    def __repr__(self):
        return self.__str__()
    

class Empty(BoardPosition):
    def __eq__(self, other):
        return self.ID == other.ID
    
    def __str__(self):
        return '-'
    
    def __repr__(self):
        return self.__str__()

##Class to controll the board
class AntGameBoard:
    """ A class to reprecent a single ant game board.
    """
    def __init__(self, size = SIDE, seed = random.randrange(1000)):
        """ Creates a number of uniformly distributed and ants and morsels on the game board.
        Will be changed to allow ants to come out of a nest.
        """
        self.size = size
        random.seed(seed)
        
        self.num_morsels = MORSEL_CNT
        morsel_positions = []
        while len(morsel_positions) < self.num_morsels:
            new_pos = (random.randrange(self.size),random.randrange(self.size))
            if not (new_pos in morsel_positions):
                morsel_positions.append(new_pos) 
        
        self.board = []
        for i in range(SIDE):
            self.board.append([])
            for j in range(SIDE):
                if (i,j) == (SIDE-1,0):
                    self.board[i].append(Ant((i,j), self, RIGHT))
                elif (i,j) in morsel_positions:
                    self.board[i].append(Morsel((i,j), self))
                else:
                    self.board[i].append(Empty((i,j), self))
                
    def __str__(self):
        """ Returns a 2-dimensional string representation of the list used as a board
        """
        r = ''
        for i in reversed(range(SIDE)):
            for j in range(SIDE):
                r += self.board[i][j].__str__() + ' '
            r += '\n'                   
        return r
    
    def __repr__(self):
        return self.__str__()
        
    def position_list(self):
        """ Returns a 1d list of the elements in the board.
        """
        return [cell for subLst in self.board for cell in subLst]
       
    def swap_position(self, (i, j), (new_i, new_j)):
        """ Swaps the given positions. Returns False if swap fails, else
        returns true.
        """
        if self.legal_position((i,j)) and self.legal_position((new_i, new_j)):
            # Do Swap
            temp = self.board[i][j]
            self.board[i][j] = self.board[new_i][new_j]
            self.board[new_i][new_j] = temp
            
            # Swap IDs
            self.board[i][j].ID = (i, j)
            self.board[new_i][new_j].ID = (new_i, new_j)
            
            # Check if morsel was eaten
            if type(self.board[i][j]) == Ant:
                if type(self.board[new_i][new_j]) == Morsel:
                    self.board[new_i][new_j] =  Empty((new_i,new_j), self)
                    self.num_morsels -= 1
            else:    
                if type(self.board[new_i][new_j]) == Ant:
                    if type(self.board[i][j]) == Morsel:
                        self.board[i][j] =  Empty((i,j), self)
                        self.num_morsels -= 1
            return True
        else:
            return False
        
    def legal_position(self, (i,j)):
        """ Checks if the given tuple is a given position.
        """
        if i < 0 or i >= self.size: return False
        if j < 0 or j >= self.size: return False
        return True
        
    def ants_in(self):
        """ Returns a list of all the ants in the given board.
        """
        ants = []
        for i in range(self.size):
            for j in range(self.size):
                if type(self.board[i][j]) == Ant:
                    ants.append(self.board[i][j])
        return ants
    
    def morsels_in(self):
        """ Returns a list of all the morsels that are left on the given board.
        """
        morsels = []
        for i in range(self.size):
            for j in range(self.size):
                if type(self.board[i][j]) == Morsel:
                    morsels.append(self.board[i][j])
        return morsels
        
    def empties_in(self):
        """ Returns a list of all the morsels that are left on the given board.
        """
        empties = []
        for i in range(self.size):
            for j in range(self.size):
                if type(self.board[i][j]) == Empty:
                    empties.append(self.board[i][j])
        return empties
        
    def peform_chromosome(self, chromosome_function):
        """ Peforms the given chromosome on the board. Returns
        true if the given board has no more Morsels.
        """
        for ant in self.ants_in():         # For each ant
            chromosome_function(ant)
            
            # Check if complete
            if self.num_morsels == 0:
                return True
        return False

## Direction functions
def index_from(direction, (x, y)):
    """ Finds the cell index in the given direction from the given position.
    """
    if      direction == UP:    return (x+1, y)
    elif    direction == DOWN:  return (x-1, y)
    elif    direction == RIGHT: return (x, y+1)
    elif    direction == LEFT:  return (x, y-1)

def turn_direction_right(direction):
    """ Turns right
    """
    if      direction == UP:    return RIGHT
    elif    direction == RIGHT: return DOWN
    elif    direction == DOWN:  return LEFT
    elif    direction == LEFT:  return UP

def turn_direction_left(direction):
    """ Turns left
    """
    if      direction == UP:    return LEFT
    elif    direction == RIGHT: return UP
    elif    direction == DOWN:  return RIGHT
    elif    direction == LEFT:  return DOWN

## Singlton to get boards
CURR_SEED = None
CURR_BOARD = None
def get_board(seed = None):
    """ Returns a board, a new board does not need to be created, only copied
    if a board was already created.
    """
    global CURR_SEED
    global CURR_BOARD
    
    if seed is None:
        if CURR_SEED is None:
            CURR_SEED = random.randrange(1000)
            CURR_BOARD = AntGameBoard(SIDE, CURR_SEED)
    else:
        if CURR_SEED <> seed:
            CURR_SEED = seed
            CURR_BOARD = AntGameBoard(SIDE, CURR_SEED)
    return deepcopy(CURR_BOARD)
